//
//  UICollectionView+ALExtension.swift
//  ALCore
//
//  Created by IdeaBank on 2023/12/18.
//


import UIKit

public extension UICollectionView {

    /// 返回collectionView所有section中的所有条数
    func numberOfItems() -> Int {
        var section = 0
        var itemsCount = 0
        while section < numberOfSections {
            itemsCount += numberOfItems(inSection: section)
            section += 1
        }
        return itemsCount
    }

    /// 刷新数据完成处理回调
    func reloadData(_ completion: @escaping () -> Void) {
        UIView.animate(withDuration: 0, animations: {
            self.reloadData()
        }, completion: { _ in
            completion()
        })
    }

    /// 使用类名出列可重用的UICollectionViewCell
    func dequeueReusableCell<T: UICollectionViewCell>(withClass desc: T.Type, for indexPath: IndexPath) -> T {
        guard let cell = dequeueReusableCell(withReuseIdentifier: String(describing: desc), for: indexPath) as? T else {
            fatalError("Couldn't find UICollectionViewCell for \(String(describing: desc)), make sure the cell is registered with collection view")
        }
        return cell
    }

    enum ElementKind  { case sectionHeader, sectionFooter }

    /// 使用类名出列可重用的UICollectionReusableView - sectionHeader
    func dequeueReusableHeader<T: UICollectionReusableView>(withClass desc: T.Type, for indexPath: IndexPath) -> T {
        return dequeueReusableSupplementaryView(ofKind: .sectionHeader, withClass: desc, for: indexPath)
    }

    /// 使用类名出列可重用的UICollectionReusableView - sectionFooter
    func dequeueReusableFooter<T: UICollectionReusableView>(withClass desc: T.Type, for indexPath: IndexPath) -> T {
        return dequeueReusableSupplementaryView(ofKind: .sectionFooter, withClass: desc, for: indexPath)
    }

    /// 使用类名出列可重用的UICollectionReusableView
    private func dequeueReusableSupplementaryView<T: UICollectionReusableView>(ofKind kind: ElementKind, withClass desc: T.Type, for indexPath: IndexPath) -> T {
        let elementKind: String
        switch kind {
        case .sectionHeader:
            elementKind = UICollectionView.elementKindSectionHeader
        case .sectionFooter:
            elementKind = UICollectionView.elementKindSectionFooter
        }
        guard let cell = dequeueReusableSupplementaryView(ofKind: elementKind, withReuseIdentifier: String(describing: desc), for: indexPath) as? T else {
            fatalError("Couldn't find UICollectionReusableView for \(String(describing: desc)), make sure the view is registered with collection view")
        }
        return cell
    }

    /// 使用类名注册UICollectionViewCell
    func register<T: UICollectionViewCell>(cellWithClass desc: T.Type) {
        register(T.self, forCellWithReuseIdentifier: String(describing: desc))
    }

    /// 使用类名注册UICollectionReusableView - SectionHeader
    func register<T: UICollectionReusableView>(headerWithClass desc: T.Type) {
        return register(supplementaryViewOfKind: .sectionHeader, withClass: desc)
    }

    /// 使用类名注册UICollectionReusableView - SectionFooter
    func register<T: UICollectionReusableView>(footerWithClass desc: T.Type) {
        return register(supplementaryViewOfKind: .sectionFooter, withClass: desc)
    }

    /// 使用类名注册UICollectionReusableView
    private func register<T: UICollectionReusableView>(supplementaryViewOfKind kind: ElementKind, withClass desc: T.Type) {
        let elementKind: String
        switch kind {
        case .sectionHeader:
            elementKind = UICollectionView.elementKindSectionHeader
        case .sectionFooter:
            elementKind = UICollectionView.elementKindSectionFooter
        }
        register(T.self, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: String(describing: desc))
    }

    /// 安全滚动到目标位置 (检查IndexPath在CollectionView中是否有效)
    func safeScrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionView.ScrollPosition, animated: Bool) {
        guard indexPath.item >= 0 &&
            indexPath.section >= 0 &&
            indexPath.section < numberOfSections &&
            indexPath.item < numberOfItems(inSection: indexPath.section) else {
                return
        }
        scrollToItem(at: indexPath, at: scrollPosition, animated: animated)
    }
}

public extension UICollectionView {

    /// 垂直方向滚动到指定的IndexPath
    @objc func scrollToVertically(_ indexPath: IndexPath, animated: Bool) {
        guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return }
        let delegate = delegate as? UICollectionViewDelegateFlowLayout

        var inset = flowLayout.sectionInset
        if let value = delegate?.collectionView?(self, layout: flowLayout, insetForSectionAt: indexPath.section) {
            inset = value
        }

        var size = flowLayout.headerReferenceSize
        if let value = delegate?.collectionView?(self, layout: flowLayout, referenceSizeForHeaderInSection: indexPath.section) {
            size = value
        }

        var point = CGPoint.zero
        if numberOfItems(inSection: indexPath.section) > .zero {
            let layoutAttrib = flowLayout.layoutAttributesForItem(at: indexPath)!
            point = CGPoint(x: .zero, y: layoutAttrib.frame.minY - inset.top - size.height)
        } else {
            guard let layoutAttrib = flowLayout.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: indexPath) else { return }
            point = CGPoint(x: .zero, y: layoutAttrib.frame.minY - inset.top - size.height)
        }

        let boundaries = point.y + bounds.size.height;
        if boundaries > contentSize.height {
            point.y = contentSize.height - bounds.height + contentInset.bottom
        }
        setContentOffset(point, animated: false)
    }

    /// 根据当前滚动位置获取所在的布局分组 (offset => section)
    @objc func verticallyLayoutSection(at point: CGPoint) -> NSNumber? {
        for (index, rect) in layoutSectionRects {
            guard rect.contains(point) else { continue }
            return NSNumber(value: index)
        }
        return nil
    }

    /// 获取每组对应的布局范围
    @objc var layoutSectionRects: [Int: CGRect] {
        let sectionRects: [Int: CGRect]
        if let existing = objc_getAssociatedObject(self, &UICollectionViewAssociatedLayoutSectionRectsKey) as? [Int: CGRect] {
            sectionRects = existing
        } else {
            sectionRects = verticallyLayoutSectionRects()
            objc_setAssociatedObject(self, &UICollectionViewAssociatedLayoutSectionRectsKey, sectionRects, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        return sectionRects
    }

    /// 重新计算每组对应的布局范围
    @objc func invalidateLayoutSectionRects() {
        objc_setAssociatedObject(self, &UICollectionViewAssociatedLayoutSectionRectsKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }

    private func verticallyLayoutSectionRects() -> [Int: CGRect] {
        guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return [:] }
        let delegate = delegate as? UICollectionViewDelegateFlowLayout

        var sectionOfRects = [Int: CGRect]()
        for section in 0..<numberOfSections {
            let indexPath = IndexPath.init(item: 0, section: section)

            var inset = flowLayout.sectionInset
            if let value = delegate?.collectionView?(self, layout: flowLayout, insetForSectionAt: indexPath.section) {
                inset = value
            }

            var size = flowLayout.headerReferenceSize
            if let value = delegate?.collectionView?(self, layout: flowLayout, referenceSizeForHeaderInSection: indexPath.section) {
                size = value
            }

            let numberOfItems = numberOfItems(inSection: section)
            if numberOfItems > .zero {
                let lastIndexPath = IndexPath(item: numberOfItems - 1, section: section)
                let lastLayoutAttrib = flowLayout.layoutAttributesForItem(at: lastIndexPath)!
                let firstLayoutAttrib = flowLayout.layoutAttributesForItem(at: indexPath)!
                var rect = firstLayoutAttrib.frame
                rect.origin.x = firstLayoutAttrib.frame.minX - inset.left
                rect.origin.y = firstLayoutAttrib.frame.minY - inset.top - size.height
                rect.size.height = lastLayoutAttrib.frame.maxY + inset.bottom - rect.origin.y
                sectionOfRects.updateValue(rect, forKey: section)
            } else {
                let layoutAttrib = flowLayout.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: indexPath)!
                sectionOfRects.updateValue(layoutAttrib.frame, forKey: section)
            }
        }
        return sectionOfRects
    }
}

private var UICollectionViewAssociatedLayoutSectionRectsKey: Void?

//MARK: - 链式属性函数
extension UICollectionView {

    /// 代理
    /// - Parameter delegate: 代理
    /// - Returns: 自身
    @discardableResult
    public func delegate(_ delegate: UICollectionViewDelegate?) -> Self {
        self.delegate = delegate
        return self
    }

    /// 数据源
    /// - Parameter dataSource: 数据源
    /// - Returns: 自身
    @discardableResult
    public func dataSource(_ dataSource: UICollectionViewDataSource?) -> Self {
        self.dataSource = dataSource
        return self
    }

    /// 预加载数据源
    /// - Parameter dataSource: 预加载数据源
    /// - Returns: 自身
    @discardableResult
    @available(iOS 10.0, *)
    public func preDataSource(_ dataSource: UICollectionViewDataSourcePrefetching?) -> Self {
        prefetchDataSource = dataSource
        return self
    }

    /// 是否允许预加载
    /// - Parameter enable: 是否允许预加载
    /// - Returns: 自身
    @discardableResult
    @available(iOS 10.0, *)
    public func isPreEnable(_ enable: Bool) -> Self {
        isPrefetchingEnabled = enable
        return self
    }

    /// 编辑状态
    /// - Parameter editing: 编辑状态
    /// - Returns: 自身
    @discardableResult
    @available(iOS 14.0, *)
    public func isEditing(_ editing: Bool) -> Self {
        isEditing = editing
        return self
    }

    /// 非编辑状态下是否可选择 cell 默认 true
    /// - Parameter allow: 是否允许
    /// - Returns: 自身
    @discardableResult
    public func allowsSelection(_ allow: Bool = true) -> Self {
        allowsSelection = allow
        return self
    }

    /// 编辑状态下是否可选择 cell 默认 false
    /// - Parameter allow: 是否允许
    /// - Returns: 自身
    @discardableResult
    @available(iOS 14.0, *)
    public func allowsSelectionDuringEditing(_ allow: Bool = false) -> Self {
        allowsSelectionDuringEditing = allow
        return self
    }

    /// 多个 cell 是否同时可被选择 默认 false
    /// - Parameter allow: 是否允许
    /// - Returns: 自身
    @discardableResult
    public func allowsMultipleSelection(_ allow: Bool = false) -> Self {
        allowsMultipleSelection = allow
        return self
    }

    /// 多个 cell 在编辑状态下是否同时可被选择 默认 false
    /// - Parameter allow: 是否允许
    /// - Returns: 自身
    @discardableResult
    @available(iOS 14.0, *)
    public func allowsMultipleSelectionDuringEditing(_ allow: Bool = false) -> Self {
        allowsMultipleSelectionDuringEditing = allow
        return self
    }

    /// 设置布局
    /// - Parameter layout: 布局对象
    /// - Returns: 自身
    @discardableResult
    public func layout(_ layout: UICollectionViewLayout) -> Self {
        collectionViewLayout = layout
        return self
    }

    /// 设置布局 默认动画
    /// - Parameters:
    ///   - layout: 布局对象
    ///   - completion: 完成事件
    ///   - animated: 是否动画
    /// - Returns: 自身
    @discardableResult
    public func layout(_ layout: UICollectionViewLayout, _ completion: ((Bool) -> Void)? = nil, animated: Bool = true) -> Self {
        setCollectionViewLayout(layout, animated: animated, completion: completion)
        return self
    }

    /// 注册 cell
    /// - Parameter cellClass: cell 类
    /// - Parameter identifier: 复用标识符
    /// - Returns: 自身
    @discardableResult
    public func register(_ cellClass: AnyClass?, identifier: String) -> Self {
        register(cellClass, forCellWithReuseIdentifier: identifier)
        return self
    }

    /// 注册头部尾部
    /// - Parameters:
    ///   - aClass: 视图类
    ///   - elementKind: 头部|尾部
    ///   - identifier: 复用标识符
    /// - Returns: 自身
    @discardableResult
    public func register(_ aClass: AnyClass?, elementKind: String, identifier: String) -> Self {
        register(aClass, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: identifier)
        return self
    }

    /// 滚动到特定的 item 默认滚动方向水平
    /// - Parameters:
    ///   - indexPath: 第几个IndexPath
    ///   - scrollPosition: 滚动方式
    ///   - animated: 是否动画 默认 true
    /// - Returns: 自身
    @discardableResult
    public func scroll(
        to indexPath: IndexPath,
        at scrollPosition: UICollectionView.ScrollPosition = .centeredHorizontally,
        animated: Bool = true
    ) -> Self {
        if indexPath.row < 0 ||
            indexPath.section < 0 ||
            indexPath.section > self.numberOfSections
            || indexPath.row > self.numberOfItems (inSection: indexPath.section) {
            return self
        }
        scrollToItem(at: indexPath, at: scrollPosition, animated: animated)
        return self
    }

    /// 滚动到特定的分区分行 默认滚动方向水平
    /// - Parameters:
    ///   - item: 第几item
    ///   - section: 第几 section
    ///   - scrollPosition: 滚动方式
    ///   - animated: 是否动画 默认 true
    /// - Returns: 自身
    @discardableResult
    public func scroll(
        item: Int,
        section: Int = 0,
        at scrollPosition: UICollectionView.ScrollPosition = .centeredHorizontally,
        animated: Bool = true
    ) -> Self {
        return scroll(to: IndexPath(item: item, section: section), at: scrollPosition, animated: animated)
    }
}

//MARK: - 初始化
extension UICollectionView {

    /// 初始化
    /// - Parameters:
    ///   - origin: origin 点
    ///   - size: size 大小
    ///   - layout: 布局对象
    public convenience init(origin: CGPoint, size: CGSize, layout: UICollectionViewLayout) {
        self.init(frame: CGRect(origin: origin, size: size), collectionViewLayout: layout)
    }

    /// 初始化
    /// - Parameters:
    ///   - x: x 坐标
    ///   - y: y 坐标
    ///   - width: 宽度大小
    ///   - height: 高度大小
    ///   - layout: 布局对象
    public convenience init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, layout: UICollectionViewLayout) {
        self.init(frame: CGRect(x: x, y: y, width: width, height: height), collectionViewLayout: layout)
    }

    /// 初始化 默认内边距 0
    /// - Parameters:
    ///   - superView: 父视图
    ///   - layout: 布局对象
    ///   - padding: 内边距
    public convenience init(superView: UIView, layout: UICollectionViewLayout, padding: CGFloat = 0) {
        self.init(frame: CGRect(
                    x: superView.AL.x + padding,
                    y: superView.AL.y + padding,
                    width: superView.AL.width - padding*2,
                    height: superView.AL.height - padding*2),
                  collectionViewLayout: layout
        )
    }
}
